package com.hp.bon.sgw.service;

import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.bon.sgw.domain.CapabilityInfo;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.util.RSAUtil;

/**
 * RSA 数字签名服务
 * 
 * @author wuzhih
 * 
 */
@Path("/")
public class RSASignService implements ConfigListener {

	private static RSASignService instance;

	public static RSASignService getInstance() {
		if (instance == null) {
			instance = new RSASignService();
			instance.init();
		}
		return instance;
	}

	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(RSASignService.class);

	private String keyFileDir = "./data/cert";
	private final String keyFile509Ext = ".ser";
	private final String keyFileParmExt = ".param";
	private volatile Map<String, PublicKey> nePublicKeyMap = Collections.emptyMap();
	/**
	 * 所关心的运行期变化的配置数据的Key
	 */
	protected final Set<String> monitorConfKeys = new HashSet<String>();
	private static String SGW_SIGN_ENABLED_KEY = "business.sgwsign.enabled";

	/**
	 * sgw 自己的私钥
	 */
	private volatile PrivateKey sgwPrivateKey;
	private String sgwPrivateKeyFile = "./etc/sgw-priv.param";
	private volatile boolean sgwSignEnabled = false;

	public RSASignService() {
		monitorConfKeys.add(SGW_SIGN_ENABLED_KEY);
	}

	public void init() {
		loadParams();
		loadNEPublicKeyFiles();
		loadSGWPrivateKey();
	}

	public String sinDATA(String requestData, String neHostID, int signType) {
		if (!this.sgwSignEnabled) {
			if (logger.isDebugEnabled()) {
				logger.debug("sgwSignEnabled is false ,so not sign");
			}
			return null;
		}
		try {

			switch (signType) {
			case CapabilityInfo.NumIdiographType_ONE: {
				byte[] sign = RSAUtil.signMessage(requestData.getBytes("iso-8859-1"), this.sgwPrivateKey);
				return RSAUtil.byte2hex(sign, false);
			}
			case CapabilityInfo.NumIdiographType_TWO: {
				PublicKey pubKey = nePublicKeyMap.get(neHostID);
				if (pubKey == null) {
					logger.warn("can't find public key for user " + neHostID + " valid sign failed ");
					return null;
				}
				byte[] sign = RSAUtil.sign2Message(requestData.getBytes("iso-8859-1"), this.sgwPrivateKey, pubKey);
				return RSAUtil.byte2hex(sign, false);
			}
			default:
				return null;
			}
		} catch (Exception e) {
			logger.warn("sinDATA err:" + e.toString());
			return null;
		}
	}

	private void loadSGWPrivateKey() {
		logger.info("load sgw private key from file " + sgwPrivateKeyFile);
		try {
			if (sgwPrivateKeyFile.endsWith(keyFileParmExt)) {

				this.sgwPrivateKey = RSAUtil.readPrivateKeyFromParamFile(sgwPrivateKeyFile);
			} else {
				this.sgwPrivateKey = RSAUtil.readPrivateKey(sgwPrivateKeyFile);
			}
		} catch (Exception e) {
			logger.warn("loadSGWPrivateKey err:" + e.toString());
		}
	}

	public boolean isSignalValid(String hostID, byte[] contents, String sign, int signType) {
		PublicKey pubKey = nePublicKeyMap.get(hostID);
		if (pubKey == null) {
			logger.warn("can't find public key for user " + hostID + " valid sign failed ");
			return false;
		}
		byte[] originSign = null;
		boolean ok = false;
		try {

			originSign = RSAUtil.hex2byte(sign);
			switch (signType) {
			case CapabilityInfo.NumIdiographType_ONE: {
				ok = RSAUtil.isValidSignal(contents, originSign, pubKey);
				break;
			}
			case CapabilityInfo.NumIdiographType_TWO: {
				ok = RSAUtil.isValidSignal2(contents, originSign, pubKey, sgwPrivateKey);
				break;
			}
			default: {
				logger.warn("not suppored sign type " + hostID + "  signType:" + signType);
				ok = true;
			}
			}
			if (logger.isDebugEnabled()) {
				String text = new String(contents, "iso-8859-1");
				String info = "check rsa sign node :" + hostID + " requestData:" + text + " len:" + text.length() + "\r\n the sign is :\r\n" + sign + "\r\n" + " RSA type " + signType + " is valid " + ok;
				logger.debug(info);
			}
			return ok;
		} catch (Exception e) {
			logger.warn("valid sign of " + hostID + "  error:" + e);
			return false;
		}

	}

	@GET
	@Path("sysctrl/reloadPublicKeyFile")
	@Produces(MediaType.TEXT_PLAIN)
	public String loadNEPublicKeyFiles() {
		logger.info("try load public key files from " + keyFileDir);
		File dir = new File(keyFileDir);
		File[] files = dir.listFiles();
		if (files == null) {
			logger.info("not key files found ");
			return "FAIL: NO KEY FILES FOUND";
		}
		Map<String, PublicKey> publicKeyMap = new LinkedHashMap<String, PublicKey>();
		for (File file : files) {
			try {
				String fileFullName = file.getCanonicalPath();
				if (file.isFile()) {
					PublicKey key = null;
					String fileType = null;
					if (fileFullName.endsWith(keyFile509Ext)) {
						fileType = keyFile509Ext;
						key = RSAUtil.readPublicKeyFromX509(fileFullName);
					} else if (fileFullName.endsWith(keyFileParmExt)) {
						fileType = keyFileParmExt;
						key = RSAUtil.readPublicKeyFromParamFile(fileFullName);
					}
					if (key != null) {
						String fileName = file.getName();
						String userName = fileName.substring(0, fileName.length() - fileType.length());
						logger.info("load public key for userName " + userName);
						publicKeyMap.put(userName, key);
					}
				}
			} catch (Exception e) {
				logger.warn("load public key err:" + e);
			}
		}
		this.nePublicKeyMap = publicKeyMap;
		loadSGWPrivateKey();
		return "SUCCESS";
	}

	public void setKeyFileDir(String keyFileDir) {
		this.keyFileDir = keyFileDir;
	}

	@Override
	public Set<String> monitorConfKeys() {
		return this.monitorConfKeys;
	}

	private void loadParams() {
		try {
			int value = SpringContext.getInstance().getConfigService().getIntValue(SGW_SIGN_ENABLED_KEY, 0);
			this.sgwSignEnabled = (value == 1);
			logger.info("sgw serverside sign enabled :" + sgwSignEnabled);
		} catch (Exception e) {
			logger.warn("load parms err:" + e.toString());
		}
	}

	@Override
	public void onConfigUpdated(String updatedKey) {
		loadParams();

	}

	public void setSgwPrivateKeyFile(String sgwPrivateKeyFile) {
		this.sgwPrivateKeyFile = sgwPrivateKeyFile;
	}

}
